package com.emn.fil.automaticdiscover.business;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;

import jpcap.NetworkInterface;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.emn.fil.automaticdiscover.dto.IP;
import com.emn.fil.automaticdiscover.dto.IPRange;
import com.emn.fil.automaticdiscover.dto.Machine;
import com.emn.fil.automaticdiscover.dto.enums.OsType;
import com.emn.fil.automaticdiscover.dto.pingjpc.PacketSender;

@Component("connection")
public class Connection {


	//Mac Ports
	@Value("${ports_mac.list}")
	private int[] ports_mac;
	private ArrayList<Integer> ports_mac_list = new ArrayList<Integer>();

	//Windows Ports
	@Value("${ports_windows.list}")
	private int[] ports_windows;
	private ArrayList<Integer> ports_windows_list = new ArrayList<Integer>();

	//Unix Ports
	@Value("${ports_unix.list}")
	private int[] ports_unix;
	private ArrayList<Integer> ports_unix_list = new ArrayList<Integer>();


	@Autowired
	private Reseau reseau;
	
	@Value("${time_out}")
	private int timeout;


	public void setTimeOut(int timeout){
		this.timeout=timeout;
	}


	/**
	 * Test connection on a range of IP between range1 to range2
	 * @param range1
	 * @param range2
	 */
	public void testConnection(IP range1, IP range2){
	
		System.out.println("Test");
		IPRange range = new IPRange(range1, range2);
		OsType os_current = OsType.UNKNOWN;
		while (range.hasNext()) {
			os_current = getOSType(range.getCurrent());
			if(os_current != OsType.UNKNOWN){
				reseau.ajoutMachine(new Machine(range.getCurrent(), os_current));
			}
			range.next();
		}
		
		System.out.println(reseau.getResume());
	}
	
	public void testConnectionPing(IP range1, IP range2, NetworkInterface device) {
		IPRange range = new IPRange(range1, range2);
		ArrayList<IP> ipConnected = new ArrayList<IP>();
		while (range.hasNext()) {
			if (new PacketSender().ping(range.getCurrent().toString(), device)) {
				ipConnected.add(range.getCurrent());
			}
			range.next();
		}
	}

	/**
	 * Test ports on IP and return the type of the machine
	 * @param ip
	 * @return 0 MAC, 1 UNIX, 2 WINDOWS, -1 UNREACHABLE
	 */
	public OsType getOSType(IP ip){
		OsType type = OsType.UNKNOWN;
		System.out.println("Test ip : "+ip.toString());
		
		/////////////////BEGIN TESTS/////////////////

		//Test windows ports
		if(testConnectionsOnPorts(ip, ports_windows_list))
			type = OsType.WINDOWS;

		//If test windows ports not succeed
		if(type == OsType.UNKNOWN){
			//Test mac ports
			if(testConnectionsOnPorts(ip, ports_mac_list))
				type = OsType.OSX;
		}

		//If test windows ports and mac ports not succeed
		if(type == OsType.UNKNOWN){
			//Test unix ports
			if(testConnectionsOnPorts(ip, ports_unix_list))
				type = OsType.UNIX;
		}

		/////////////////END TESTS/////////////////
		return type;
	}

	public void initArrayList() {
		// fill the arrayLists with the content of the hashes
		for(int i=0;i<ports_windows.length;i++)
			ports_windows_list.add(ports_windows[i]);
		for(int i=0;i<ports_mac.length;i++)
			ports_mac_list.add(ports_mac[i]);
		for(int i=0;i<ports_unix.length;i++)
			ports_unix_list.add(ports_unix[i]);
	}

	/**
	 * Test connection on ip with the array of ports
	 * @param ip
	 * @param ports
	 * @return boolean state
	 */
	private boolean testConnectionsOnPorts(IP ip,ArrayList<Integer> ports) {
		InputStream input   = null;
		int port;
		Socket socket = null;

		//BEGIN TEST OF CONNECTION
		for(int i = 0; i < ports.size();i++)
		{
			//System.out.println("Test port : "+ports[i]);
			try
			{
				port   = ports.get(i);
				SocketAddress sockaddr = new InetSocketAddress(ip.toString(), port);
				socket = new Socket();
				socket.connect(sockaddr, this.timeout);

				// Open stream
				input = socket.getInputStream();
				System.out.println("Connection ok ! : "+ports.get(i));

				// Show the server response
				//String response = new BufferedReader(new InputStreamReader(input)).readLine();
				//System.out.println("Server message: " + response);

				try
				{
					input.close();
					socket.close();
				}
				catch (Exception e)
				{
					//System.err.println("[ERROR] close socket : "+e);
				}

				return true;
			}
			catch (UnknownHostException e)
			{
				//System.out.println("Connection fail !");
				//System.err.println("UnknownHostException : "+e);
			}
			catch (IOException e)
			{
				//System.out.println("Connection fail !");
				//System.err.println("IO : "+e);
			}
			finally
			{
				try
				{
					input.close();
					socket.close();
				}
				catch (Exception e)
				{
					//System.out.println("Connection fail !");
					//System.err.println("Finally : "+e);
				}
			}
		}
		return false;
	}
	
	public void clearArrays() {
		this.ports_mac_list.clear();
		this.ports_windows_list.clear();
		this.ports_unix_list.clear();
	}
	
	public void setPorts_mac_list(Integer e) {
		this.ports_mac_list.add(e);
	}

	public void setPorts_windows_list(Integer e) {
		this.ports_windows_list.add(e);
	}

	public void setPorts_unix_list(Integer e) {
		this.ports_unix_list.add(e);
	}

	public ArrayList<Integer> getPorts_mac() {
		return ports_mac_list;
	}

	public ArrayList<Integer> getPorts_windows() {
		return ports_windows_list;
	}

	public ArrayList<Integer> getPorts_unix() {
		return ports_unix_list;
	}

	public int getTimeout() {
		return timeout;
	}

	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

	public Reseau getReseau() {
		return reseau;
	}

}
